package agents;

import java.util.ArrayList;
import aglobe.container.agent.Agent;
import aglobe.container.transport.InvisibleContainerException;
import aglobe.ontology.AgentInfo;
import aglobe.ontology.Message;
import aglobe.ontology.MessageConstants;
import model.Model;
import model.Model.Directions;
import model.info.Constants;
import model.info.Coordinate;
import model.mapping.MapPoint;
import model.route.AstarRoute;
import model.route.Route;
import model.route.RouteStore;
import model.route.Step;
import model.simulation.Point3D;

public class RouteAgent extends Agent {
	
	private Model model;
	private RouteStore routeStore;

	public void init(AgentInfo info, int initState) {
		
		routeStore = new RouteStore();
	}
	
	public void handleIncomingMessage(Message message) {
		
		if (message.getPerformative().equals(MessageConstants.INFORM)) {
			if (message.getContent() instanceof String) 
				if (message.getContent().equals(Constants.KILL))
					finishExecution();
		}
	}

	public RouteStore getRouteStore() {
		return routeStore;
	}

	public void setRouteStore(RouteStore routeStore) {
		this.routeStore = routeStore;
	}

	public void execRouteAgent(Model model) {
		
		this.model = model;
		generateRoutesForDrivers();
	}
	
	public void generateRoutesForDrivers() {
		
		//IndividualsAgent individuals =  model.getTrafficAgent().getIndividualsAgent();
		PointRouteAgent points = model.getTrafficAgent().getPointRouteAgent();
		//Integer drivers = individuals.getDriverAgents().size();
		ArrayList<Point3D> inputs = new ArrayList<Point3D>();
		inputs.addAll(points.getInEast());
		inputs.addAll(points.getInNorth());
		inputs.addAll(points.getInSouth());
		inputs.addAll(points.getInWest());
		ArrayList<Point3D> outputs = new ArrayList<Point3D>();
		outputs.addAll(points.getOutWest());
		outputs.addAll(points.getOutSouth());
		outputs.addAll(points.getOutNorth());
		outputs.addAll(points.getOutEast());
		System.out.println("Rutas: "+inputs.size() * outputs.size());
		//for (int i=0;i<3;i++)
			for (int i=0;i<1;i++) {
				configuredRoute(inputs.get(i),outputs.get(i));
				/*RouteRunnable runnableRoutes = new RouteRunnable(inputs.get(i),outputs.get(j));
				Thread routesThread = new Thread(runnableRoutes);
				routesThread.start();*/
			}
		sendRoutesCalculated();
	}
	
	public void configuredRoute(Point3D in,Point3D out) {
		
		Integer test = 0;
		Route route = null;
		Boolean correct = false;
		int direction = model.getModelMapBuilder().getDriverMap()[in.x][in.y].
			getDataList().get(in.z).getDirection().intValue();
		do {
			Coordinate begin = model.getModelMapBuilder().getDriverMap()[in.x][in.y].getCoordinate();
			Coordinate end = model.getModelMapBuilder().getDriverMap()[out.x][out.y].getCoordinate();//getSecond();
			Coordinate wayPoint = getWayPoint(in.x,in.y,test);
			route = new Route(model,begin.toString(),wayPoint.toString(),end.toString());
			if (route.getCurrentStep() != null && isAceptableRoute(direction,route.getCurrentStep()))
				correct = true;
			else
				test = test + 1;
		} while (!correct && test < 20);
		
		if (correct) {
			ArrayList<MapPoint> path = buildRoutePath(route,in,out);
			if (!path.isEmpty()) 
				routeStore.getRoutes().add(path);
		}	
		/*
		ArrayList<MapPoint> path = buildRoutePath(in,out);
		if (!path.isEmpty()) 
			routeStore.getRoutes().add(path);*/
	}
	
	public Coordinate getWayPoint(int x,int y,Integer intent) {
		
		Coordinate resultado = null;
		int max = 0;
		int posx = -1;
		int posy = -1;
		int limit = intent*10;
		
		for (int i=x-limit;i<=x+limit;i++)
			for (int j=y-limit;j<=y+limit;j++) {
				if (model.getMapCheckouts().isInMap(i,j) && !model.getMapCheckouts().isSamePlace(x,y,i,j) &&
					model.getModelMapBuilder().getDriverMap()[i][j].isRoad()) {
						int dx = Math.abs(x-i);
						int dy = Math.abs(y-j);
						int sum = dx+dy;
						if (max < sum) {
							posx = i;
							posy = j;
							max = sum;
						}
				}	
			}
		if (posx == -1 && posy == -1)
			resultado = model.getModelMapBuilder().getDriverMap()[x][y].getCoordinate();
		else
			resultado = model.getModelMapBuilder().getDriverMap()[posx][posy].getCoordinate();
		return resultado;
	}
	
	public Boolean isAceptableRoute(int direction,Step current) {
		
		Boolean correct = false;
		String step = current.getInstruccion();
		if (step.contains(Constants.NORTH)) {
			if (direction == Directions.UP.ordinal() || 
				direction == Directions.RIGHTUP.ordinal() || 
				direction == Directions.LEFTUP.ordinal())
				correct = true;
		}
		else if (step.contains(Constants.SOUTH)) {
			if (direction == Directions.DOWN.ordinal() || 
				direction == Directions.RIGHTDOWN.ordinal() || 
				direction == Directions.LEFTDOWN.ordinal())
				correct = true;
		}
		else if (step.contains(Constants.EAST)) {
			if (direction == Directions.RIGHT.ordinal() || 
				direction == Directions.RIGHTUP.ordinal() || 
				direction == Directions.RIGHTDOWN.ordinal())
				correct = true;
		}
		else if (step.contains(Constants.WEST)) {
			if (direction == Directions.LEFT.ordinal() || 
				direction == Directions.LEFTUP.ordinal() || 
				direction == Directions.LEFTDOWN.ordinal())
				correct = true;
		}
		return correct;
	}
	
	public ArrayList<MapPoint> buildRoutePath(Point3D in,Point3D out) {
		
		ArrayList<MapPoint> path = new ArrayList<MapPoint>();
		int x = in.x;
		int y = in.y;
		path.add(model.getModelMapBuilder().getDriverMap()[x][y]);
		x = out.x;
		y = out.y;
		MapPoint begin = model.getModelMapBuilder().getDriverMap()[x][y];
		MapPoint end = model.getModelMapBuilder().getDriverMap()[x][y];
		AstarRoute astarRoute = new AstarRoute(model,begin,end);
		path = astarRoute.getAstarRoute();
		return path;
	}
	
	public ArrayList<MapPoint> buildRoutePath(Route route,Point3D point,Point3D point2) {
		
		ArrayList<MapPoint> path = new ArrayList<MapPoint>();
		int x = point.x;
		int y = point.y;
		path.add(model.getModelMapBuilder().getDriverMap()[x][y]);
		/*for (int i=step;i<route.getPath().size()&&!found;i++) {
			System.out.println(route.getPath().get(step));
			MapPoint begin = path.get(path.size()-1);
			MapPoint end = route.getPath().get(step).getFinishPoint();
			AstarRoute astarRoute = new AstarRoute(model,begin,end);
			section = astarRoute.getAstarRoute();
			if (section != null) {
				for (int n=0;n<section.size();n++)
					System.out.println(section.get(n));
				path.addAll(section);
				step = step +1;
			}
			else
				found = true;
		}*/
		MapPoint begin = path.get(0);
		MapPoint end = route.getPath().get(route.getPath().size()-1).getFinishPoint();
		System.out.println(end);
		System.out.println(model.getModelMapBuilder().getDriverMap()[point2.x][point2.y]);
		AstarRoute astarRoute = new AstarRoute(model,begin,end);
		path = astarRoute.getAstarRoute();
		return path;
	}
	
	public void sendRoutesCalculated() {
		
		Message sms = Message.newInstance();
		sms.setPerformative(MessageConstants.INFORM_DONE);
		sms.setSender(getAddress());
		sms.setReceiver(model.getTrafficAgent().getAddress());
		try {
			sendMessage(sms);
		} catch (InvisibleContainerException e) {
			e.printStackTrace();
		}
	}
	
	public void finishExecution() {
		
		try {
			System.out.println("Generador de routas muerto");
			kill();
			finish();
		} catch (Throwable t) {
			t.printStackTrace();
		}
	}
}
